﻿Option Explicit On

Imports System.Windows.Controls.Canvas

Public Class shpBlock

#Region "Events declarations"
    Public Event Move(ByVal sender As Object, ByVal X As Integer, ByVal Y As Integer)
    Public Event Resize(ByVal sender As Object, ByVal Width As Integer, ByVal Height As Integer)
    Public Event Destruct(ByVal sender As Object)
    Public Event CreatedActivator(ByVal sender As Object, ByVal control As clsActivator)
    Public Event DeletedActivator(ByVal sender As Object, ByVal control As clsActivator)
    Public Event CreatedEvent(ByVal sender As Object, ByVal control As clsEvent)
    Public Event DeletedEvent(ByVal sender As Object, ByVal control As clsEvent)
    Public Event CreatedInput(ByVal sender As Object, ByVal control As clsInput)
    Public Event DeletedInput(ByVal sender As Object, ByVal control As clsInput)
    Public Event CreatedOutput(ByVal sender As Object, ByVal control As clsOutput)
    Public Event DeletedOutput(ByVal sender As Object, ByVal control As clsOutput)
    Public Event UnloadedActivator(ByVal sender As Object, ByVal control As clsActivator)
    Public Event UnloadedEvent_(ByVal sender As Object, ByVal control As clsEvent)
    Public Event UnloadedInput(ByVal sender As Object, ByVal control As clsInput)
    Public Event UnloadedOutput(ByVal sender As Object, ByVal control As clsOutput)
    Public Event ActivatorDoubleClick(ByVal sender As Object, ByVal e As MouseButtonEventArgs)
    Public Event EventDoubleClick(ByVal sender As Object, ByVal e As MouseButtonEventArgs)
    Public Event InputDoubleClick(ByVal sender As Object, ByVal e As MouseButtonEventArgs)
    Public Event OutputDoubleClick(ByVal sender As Object, ByVal e As MouseButtonEventArgs)
#End Region

    Private m_Type As Byte
    Private m_Ancestor As shpEditor
    Private WithEvents m_Label As clsLabel
    Private m_Key As String
    Private m_Resize As Boolean
    Private m_Objects As Long
    Private m_PntClick As Point, m_PntAccur As Point
    Private m_Width As Double, m_Height As Double, m_Left As Double, m_Top As Double

    Private Activators As New Collection
    Private Events As New Collection
    Private Inputs As New Collection
    Private Outputs As New Collection

#Region "properties"
    ' Тип объекта
    Public ReadOnly Property Type() As Integer
        Get
            Return m_Type
        End Get
    End Property
    ' Ключ, с которым блок хранится у родителя
    Public ReadOnly Property Key() As String
        Get
            Key = m_Key
        End Get
    End Property
    ' Родитель блока
    Public ReadOnly Property Ancestor() As shpEditor
        Get
            Ancestor = m_Ancestor
        End Get
    End Property
    ' Прямоугольник в блоке
    Public ReadOnly Property Shape() As Rectangle
        Get
            Return Block
        End Get
    End Property
    ' Координата X последнего нажатия на блоке
    Public ReadOnly Property ClickX() As Integer
        Get
            ClickX = m_PntClick.X
        End Get
    End Property
    ' Координата Y последнего нажатия на блоке
    Public ReadOnly Property ClickY() As Integer
        Get
            ClickY = m_PntClick.Y
        End Get
    End Property
    ' Нормированный размер ширины
    Public ReadOnly Property nWidth() As Double
        Get
            nWidth = m_Width
        End Get
    End Property
    ' Нормированный размер высоты
    Public ReadOnly Property nHeight() As Double
        Get
            nHeight = m_Height
        End Get
    End Property
    ' Нормированный отступ слева
    Public ReadOnly Property nLeft() As Double
        Get
            nLeft = m_Left
        End Get
    End Property
    ' Нормированный отступ сверху
    Public ReadOnly Property nTop() As Double
        Get
            nTop = m_Top
        End Get
    End Property
    ' Коллекция Activators
    Public ReadOnly Property GetActivators() As Collection
        Get
            GetActivators = Activators
        End Get
    End Property
    ' Коллекция Events
    Public ReadOnly Property GetEvents() As Collection
        Get
            GetEvents = Events
        End Get
    End Property
    ' Коллекция Inputs
    Public ReadOnly Property GetInputs() As Collection
        Get
            GetInputs = Inputs
        End Get
    End Property
    ' Коллекция Outputs
    Public ReadOnly Property GetOutputs() As Collection
        Get
            GetOutputs = Outputs
        End Get
    End Property
    ' Надпись блока
    Public Property Label() As String
        Get
            Return m_Label.Text
        End Get
        Set(ByVal value As String)
            m_Label.Text = value
        End Set
    End Property
    ' Заливка блока
    Public Property Fill() As Brush
        Get
            Return Block.Fill
        End Get
        Set(ByVal value As Brush)
            Block.Fill = value
        End Set
    End Property
#End Region
    ' Конструктор
    Public Sub New(ByVal ancestor As shpEditor, ByVal key As String)
        m_Ancestor = ancestor
        m_Key = key
        InitializeComponent()
    End Sub
    ' Добавление activator
    Public Function AddActivator() As clsActivator
        Dim shp As clsActivator, key As String
        key = UniqueKey()
        shp = New clsActivator(Me, key)
        Activators.Add(shp, key)
        Container.Children.Add(shp)
        DrawActivators()
        shp.Visibility = Windows.Visibility.Visible
        AddActivator = shp
        AddHandler shp.Unloaded, AddressOf shpActivator_Unload
        AddHandler shp.PortDoubleClick, AddressOf shpActivator_DoubleClick
        RaiseEvent CreatedActivator(Me, shp)
    End Function
    ' Добавление event
    Public Function AddEvent() As clsEvent
        Dim shp As clsEvent, key As String
        key = UniqueKey()
        shp = New clsEvent(Me, key)
        Events.Add(shp, key)
        Container.Children.Add(shp)
        DrawEvents()
        shp.Visibility = Windows.Visibility.Visible
        AddEvent = shp
        AddHandler shp.Unloaded, AddressOf shpEvent_Unload
        AddHandler shp.PortDoubleClick, AddressOf shpEvent_DoubleClick
        RaiseEvent CreatedEvent(Me, shp)
    End Function
    ' Добавление Input
    Public Function AddInput() As clsInput
        Dim shp As clsInput, key As String
        key = UniqueKey()
        shp = New clsInput(Me, key)
        Inputs.Add(shp, key)
        Container.Children.Add(shp)
        DrawInputs()
        shp.Visibility = Windows.Visibility.Visible
        AddInput = shp
        AddHandler shp.Unloaded, AddressOf shpInput_Unload
        AddHandler shp.PortDoubleClick, AddressOf shpInput_DoubleClick
        RaiseEvent CreatedInput(Me, shp)
    End Function
    ' Добавление Output
    Public Function AddOutput() As clsOutput
        Dim shp As clsOutput, key As String
        key = UniqueKey()
        shp = New clsOutput(Me, key)
        Outputs.Add(shp, key)
        Container.Children.Add(shp)
        DrawOutputs()
        shp.Visibility = Windows.Visibility.Visible
        AddOutput = shp
        AddHandler shp.Unloaded, AddressOf shpOutput_Unload
        AddHandler shp.PortDoubleClick, AddressOf shpOutput_DoubleClick
        RaiseEvent CreatedOutput(Me, shp)
    End Function
    ' Перемещение блока
    Public Sub MoveShape(ByVal x As Integer, ByVal y As Integer)
        SetLeft(Me, x)
        SetTop(Me, y)
        m_Left = x / m_Ancestor.ZoomX
        m_Top = y / m_Ancestor.ZoomY
        RaiseEvent Move(Me, x, y)
    End Sub
    ' Удаление блока, удаление порта блока
    Public Sub Remove(ByVal shp As Object)
        Select Case shp.Type
            Case c_Block
                m_Ancestor.Remove(Me)
                RaiseEvent Destruct(Me)
            Case c_Activator
                Container.Children.Remove(shp)
                Activators.Remove(shp.key)
                DrawActivators()
                RaiseEvent DeletedActivator(Me, shp)
            Case c_Event
                Container.Children.Remove(shp)
                Events.Remove(shp.key)
                DrawEvents()
                RaiseEvent DeletedEvent(Me, shp)
            Case c_Input
                Container.Children.Remove(shp)
                Inputs.Remove(shp.key)
                DrawInputs()
                RaiseEvent DeletedInput(Me, shp)
            Case c_Output
                Container.Children.Remove(shp)
                Outputs.Remove(shp.key)
                DrawOutputs()
                RaiseEvent DeletedOutput(Me, shp)
        End Select
    End Sub

    Private Sub DrawActivators()
        Dim i As Long, shp As clsActivator, cnt As Long, hei As Double
        cnt = Activators.Count + 1
        hei = cnt * 1.5 * c_PortHeight
        Block.MinHeight = hei
        If Block.ActualHeight < hei And hei > c_BlockHeight * m_Ancestor.ZoomY Then
            Block.Height = hei
        End If
        For Each shp In Activators
            i += 1
            shp.MoveShape(-c_PortWidth / 2, i * Block.ActualHeight / cnt - c_PortHeight / 2)
        Next shp
    End Sub

    Private Sub DrawEvents()
        Dim i As Long, shp As clsEvent, cnt As Long, hei As Double
        cnt = Events.Count + 1
        hei = cnt * 1.5 * c_PortHeight
        Block.MinHeight = hei
        If Block.ActualHeight < hei And hei > c_BlockHeight * m_Ancestor.ZoomY Then
            Block.Height = hei
        End If
        For Each shp In Events
            i += 1
            shp.MoveShape(Block.ActualWidth - c_PortWidth / 2, i * Block.ActualHeight / cnt - c_PortHeight / 2)
        Next shp
    End Sub

    Private Sub DrawInputs()
        Dim i As Long, shp As clsInput, cnt As Long, wid As Double
        cnt = Inputs.Count + 1
        wid = cnt * 1.5 * c_PortHeight
        Block.MinWidth = wid
        If Block.ActualWidth < wid And wid > c_BlockWidth * m_Ancestor.ZoomX Then
            Block.Width = wid
        End If
        For Each shp In Inputs
            i += 1
            shp.MoveShape(i * Block.ActualWidth / cnt - c_PortWidth / 2, -c_PortHeight / 2)
        Next shp
    End Sub

    Private Sub DrawOutputs()
        Dim i As Long, shp As clsOutput, cnt As Long, wid As Double
        cnt = Outputs.Count + 1
        wid = cnt * 1.5 * c_PortHeight
        Block.MinWidth = wid
        If Block.ActualWidth < wid And wid > c_BlockWidth * m_Ancestor.ZoomX Then
            Block.Width = wid
        End If
        For Each shp In Outputs
            i += 1
            shp.MoveShape(i * Block.ActualWidth / cnt - c_PortWidth / 2, Block.ActualHeight - c_PortHeight / 2)
        Next shp
    End Sub

    Private Sub Draw()
        DrawActivators()
        DrawEvents()
        DrawInputs()
        DrawOutputs()
    End Sub

    'Генерация уникального ключа
    Private Function UniqueKey() As String
        m_Objects += 1
        UniqueKey = m_Key & "." & m_Objects.ToString
    End Function

#Region "events"
    Private Sub shpBlock_Initialized(ByVal sender As Object, ByVal e As EventArgs) Handles Block.Initialized
        m_Type = c_Block
        Block.Width = c_BlockWidth * m_Ancestor.ZoomX
        Block.Height = c_BlockHeight * m_Ancestor.ZoomY
        Block.Fill = ColorBlock
        Block.Stroke = ColorStroke
        Block.StrokeThickness = 1
        Block.RadiusX = c_BlockRadius
        Block.RadiusY = c_BlockRadius
        m_Label = New clsLabel(Me)
        m_Label.Text = "New Block"
        Container.Children.Add(m_Label)
    End Sub

    Private Sub shpBlock_SizeChanged(ByVal sender As Object, ByVal e As SizeChangedEventArgs) Handles Block.SizeChanged
        If Block.ActualWidth < c_BlockWidth * m_Ancestor.ZoomX Then
            Block.Width = c_BlockWidth * m_Ancestor.ZoomX
        Else
            m_Width = Block.ActualWidth / m_Ancestor.ZoomX
        End If
        If Block.ActualHeight < c_BlockHeight * m_Ancestor.ZoomY Then
            Block.Height = c_BlockHeight * m_Ancestor.ZoomY
        Else
            m_Height = Block.ActualHeight / m_Ancestor.ZoomY
        End If
        Draw()
        RaiseEvent Resize(Me, Block.ActualWidth, Block.ActualHeight)
    End Sub

    Private Sub shpBlock_MouseLeftButtonDown(ByVal sender As Object, ByVal e As MouseButtonEventArgs) Handles Block.MouseLeftButtonDown, m_Label.MouseLeftButtonDown
        Dim x As Integer, y As Integer
        x = e.GetPosition(Me).X : y = e.GetPosition(Me).Y
        m_PntClick.X = x : m_PntClick.Y = y
        If (sender Is m_Label) And (Not m_Label.IsReadOnly) Then
            Exit Sub
        End If
        If x > Block.Width - c_BlockResize Or y > Block.ActualHeight - c_BlockResize Then
            m_Resize = True
            m_PntAccur.X = Block.ActualWidth - x
            m_PntAccur.Y = Block.ActualHeight - y
        Else
            m_Ancestor.DragObject = Me
        End If
        Focus()
    End Sub

    Private Sub shpBlock_MouseLeftButtonUp(ByVal sender As Object, ByVal e As MouseButtonEventArgs) Handles Block.MouseLeftButtonUp, m_Label.MouseLeftButtonUp
        m_Resize = False
    End Sub

    Private Sub shpBlock_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs) Handles Block.MouseMove
        Dim cur As Point, x As Integer, y As Integer
        x = e.GetPosition(Me).X : y = e.GetPosition(Me).Y
        cur.X = Block.ActualWidth - c_BlockResize
        cur.Y = Block.ActualHeight - c_BlockResize
        If m_Ancestor.DragObject IsNot Nothing Then
            Cursor = Cursors.Hand
        Else
            If x > cur.X Then
                If y > cur.Y Then
                    Cursor = Cursors.SizeNWSE
                Else
                    Cursor = Cursors.SizeWE
                End If
            ElseIf y > cur.Y Then
                Cursor = Cursors.SizeNS
            Else
                Cursor = Cursors.Arrow
            End If
            If m_Resize Then
                Block.Width = x + m_PntAccur.X
                Block.Height = y + m_PntAccur.Y
            End If
        End If
    End Sub

    Private Sub shpBlock_MouseEnter(ByVal sender As Object, ByVal e As MouseEventArgs) Handles Me.MouseEnter
        Block.StrokeThickness = c_BorderEnter
    End Sub

    Private Sub shpBlock_MouseLeave(ByVal sender As Object, ByVal e As MouseEventArgs) Handles Me.MouseLeave
        Block.StrokeThickness = c_BorderLeave
        m_Resize = False
    End Sub

    Private Sub shpBlock_GotFocus(ByVal sender As Object, ByVal e As RoutedEventArgs) Handles Me.GotFocus
        SetZIndex(Me, 3)
        Opacity = 0.8
        Block.Stroke = ColorFocus
        m_Ancestor.FocusObject = Me
    End Sub

    Private Sub shpBlock_LostFocus(ByVal sender As Object, ByVal e As RoutedEventArgs) Handles Me.LostFocus
        SetZIndex(Me, 2)
        Opacity = 1
        Block.Stroke = ColorStroke
    End Sub

    Private Sub shpBlock_KeyDown(ByVal sender As Object, ByVal e As KeyEventArgs) Handles Me.KeyDown
        If m_Ancestor.FocusObject Is Me Then
            If Keyboard.Modifiers = ModifierKeys.Control Then
                Select Case e.Key
                    Case Input.Key.A
                        AddActivator()
                    Case Input.Key.E
                        AddEvent()
                    Case Input.Key.I
                        AddInput()
                    Case Input.Key.O
                        AddOutput()
                End Select
            Else
                Select Case e.Key
                    Case Input.Key.F2
                        m_Label.Edit()
                    Case Input.Key.Delete
                        Remove(Me)
                End Select
            End If
        End If
    End Sub

    Private Sub shpLabel_Loaded(ByVal sender As Object, ByVal e As RoutedEventArgs) Handles m_Label.Loaded
        m_Label.ContextMenu = Block.ContextMenu
    End Sub

    Private Sub shpLabel_LayoutUpdated() Handles m_Label.SizeChanged, Me.Resize
        m_Label.Width = Block.ActualWidth - c_PortWidth
        m_Label.MoveShape(c_PortWidth / 2, (Block.ActualHeight - m_Label.ActualHeight) / 2)
    End Sub

    Private Sub itmActivator_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
        AddActivator()
    End Sub

    Private Sub itmEvent_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
        AddEvent()
    End Sub

    Private Sub itmInput_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
        AddInput()
    End Sub

    Private Sub itmOutput_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
        AddOutput()
    End Sub

    Private Sub itmRemove_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
        Remove(Me)
    End Sub

    Private Sub itmEdit_Click(ByVal sender As Object, ByVal e As RoutedEventArgs) Handles m_Label.MouseDoubleClick
        m_Label.Edit()
    End Sub
#End Region

#Region "childs events"
    Private Sub shpActivator_Unload(ByVal sender As Object, ByVal e As RoutedEventArgs)
        RaiseEvent UnloadedActivator(Me, sender)
    End Sub

    Private Sub shpEvent_Unload(ByVal sender As Object, ByVal e As RoutedEventArgs)
        RaiseEvent UnloadedEvent_(Me, sender)
    End Sub

    Private Sub shpInput_Unload(ByVal sender As Object, ByVal e As RoutedEventArgs)
        RaiseEvent UnloadedInput(Me, sender)
    End Sub

    Private Sub shpOutput_Unload(ByVal sender As Object, ByVal e As RoutedEventArgs)
        RaiseEvent UnloadedOutput(Me, sender)
    End Sub

    Private Sub shpActivator_DoubleClick(ByVal sender As Object, ByVal e As MouseButtonEventArgs)
        RaiseEvent ActivatorDoubleClick(sender, e)
    End Sub

    Private Sub shpEvent_DoubleClick(ByVal sender As Object, ByVal e As MouseButtonEventArgs)
        RaiseEvent EventDoubleClick(sender, e)
    End Sub

    Private Sub shpInput_DoubleClick(ByVal sender As Object, ByVal e As MouseButtonEventArgs)
        RaiseEvent InputDoubleClick(sender, e)
    End Sub

    Private Sub shpOutput_DoubleClick(ByVal sender As Object, ByVal e As MouseButtonEventArgs)
        RaiseEvent OutputDoubleClick(sender, e)
    End Sub
#End Region

End Class

